雖然系統中沒有資料也可以正常運作,不過個人還是習慣先準備好資料,再一層層往上建起來,若跟大家習慣不同還請多包涵
在 Laravel 中,資料庫相關的類別主要有下列三項:
php artisan make:model <Model 名稱> -m
-m
帶表順帶生成 migration 檔案app\
,筆者習慣生成一個 Models
資料夾存放,若有移動請修改檔案內的 namespacedatabase\migrations\
php artisan make:resource <Model 名稱>
app\Http\Resources\
Post:
php artisan make:model Post -m
php artisan make:resource Post
下面是我們所產出的 post migration,其中 class name 的語意明確,告訴我們這個 class 就是用來產生 posts
這張表。
class CreatePostsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('posts');
}
}
重點整理如下:
01. function up(): 當執行指令 php artisan migrate
時會對資料表進行的操作。
02. function down(): 當使用 Laravel Facades "Schema" 刪除資料表時候執行
03. Laravel 資料表欄位習慣以 snake_case 方式命名
04. schema 內各種欄位類型與設定可以參考官方列表,這裡舉例幾個常用的:
// 加入 `created_at` 和 `updated_at` 兩個欄位,當資料新增或修改時,會自動帶入時間值
$table->timestamps();
// 加入 `deleted_at` 作為 soft deletes 欄位
$table->softDeletes();
// 最後呼叫 nullable() 欄位設為空值
$table->string('欄位名稱')->nullable();
// 最後呼叫 unique() 指定單一欄位為唯一
$table->string('欄位名稱')->unique();
// 複合欄位指定為唯一
$table->unique([ '欄位名稱 01', '欄位名稱 02', ... ]);
// 指定為 foreign key
// 1. 先建立欄位
$table->integer('欄位名稱')->unsigned();
// 2. 指定 foreign key 資料
$table->foreign('欄位名稱')->references('對應欄位')->on('對應 table');
php artisan migrate
posts table 範例設計
Schema::create('posts', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('user_id')->unsigned();
$table->string('title');
$table->text('content');
$table->dateTime('published_at');
$table->timestamps();
$table->softDeletes();
$table->foreign('user_id')->references('id')->on('users');
});
在使用 migration 的經驗上,初始的建立以及各種欄位設定,code-first 的作法個人覺得滿好用的,但在修改欄位後要更新 schema 的部分,一開始會因為習慣的問題感覺沒有很好用。舉例來說,假設 posts 已經建立並存在資料,這個時候假設我們要刪除 published_at
欄位,所需要進行的步驟為:
php artisan make:migration drop_publish_at_to_users
// ....
public function up()
{
Schema::table('posts', function (Blueprint $table) {
$table->dropColumn('published_at');
});
}
// ...
php artisan migrate
從上面的流程可以知道,migration 其實是一個針對 db 操作的一個 code-first 的紀錄檔,他會轉為一串 SQL 語法,這大概也是為甚麼每個 migration 檔案名稱都有時間戳記,只是像我這種資質駑鈍的新手村菜鳥,每一次都要有一個檔案紀錄,一開始也真的滿不習慣的,但用久了就認命了 XD。
預設產出的 Model 檔如下,在沒有任何設定上,即可直接使用、存取資料表資料。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
//
}
官網內有更詳細的介紹各種客製的資料,包括 資料表名稱 ($table)
、主鍵 ($primaryKey)
, 是否使用建立/更新日期 ($timestamps)
和 連線 ($connection)
...等,這邊就不贅述。
上述基本設定之外,可以在 model 中客製一些方法,讓我們更容易使用。例如在之前 post migration 中我們定義了 published_at
欄位,在某些情況下我們會需要知道該篇 post 當下是否已經上架,這時候我們會在業務邏輯的部分寫如下面的判斷:
$today = Carbon::today();
$isPublished = $today->gte($post->published_at);
if ($isPublished) {
// ...
假若這樣的判斷是很常用在各種業務邏輯當中,我們就可以將此邏輯寫入 model 當中:
class Post extends Model
{
public function isPublished() {
$today = Carbon::today();
return $today->gte($this->published_at);
}
實際運用的時候就可以簡化如下,也更方便維護
if ($post->isPublished) {
// ...
寫資料庫哪有不複雜的關聯呢! 預設情況下,model 的物件,並不會將關聯的 model 資料一起帶出來。以我們目前的 Post 物件來說,只有會有 user_id
的資料,但並不會把該 user 的資料一起帶出來。因此若有需求,可以在 model 裡面加上關聯的 function:
public function user() {
return $this->belongsTo('App\Models\User');
}
實際運用上就相對更方便
// $post->user 的結果會是 User model 的實例
$userNameOfPost = $post->user->name;
關聯重點整理:
function user()
預設要有 user_id
foreign key。若有特殊鍵值,需要額外設定。'App\Models\User'
) 作為參數。在 Laravel 官方文件中,Resource 是為了 API 將 model 資料輸出時轉換為 php array 時候用。
預設產出的 resource 類別會有個 toArray 方法讓我們進行改寫:
class Post extends JsonResource
{
/**
* Transform the resource into an array.
*
* @param \Illuminate\Http\Request $request
* @return array
*/
public function toArray($request)
{
return parent::toArray($request);
}
}
Resource 會帶有 model 本身的資料,因此我們改寫的時可以依照 API 需求自行調整要輸出的結果 (尤其是將 snake_case 轉換為 camel case 很好用),例如 Post Resource 我們改寫如下:
public function toArray($request)
{
return [
"id" => $this->id,
"title" => $this->title,
"content" => $this->content,
"updatedAt" => $this->updated_at,
"author" => this->user,
];
在回傳結果前,我們就可以利用 Resource 類別將 model 資料轉換:
// ...
use App\Http\Resources\Post as PostResource;
// ...
public function getPost() {
// ...
return new UserResource($post);
以上就是資料庫相關的三個主要類別,雖然有點多,但是頭過身就過~ 明天在進入 Repository 和 Service 之前,先來看看 Laravel 的 injection (依賴注入) 吧!